import threading
from typing import Callable

from seleniumwire import webdriver
import settings
from common.core.meta import BASE_PAGE, PAGEOBJECT_BASE_ATTRS, PAGEOBJECT_DEFAULT_VAL, PageObjectBase
from common.utils.load import load_pkg_modules


class _NotImplement:
    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, instance: "PageObject", owner: type):
        raise NotImplementedError(f"{self.name}尚未实现具体方法")


class PageObject(PageObjectBase):
    get: Callable = _NotImplement()
    redirect: Callable = _NotImplement()
    quit: Callable = _NotImplement()
    find: Callable = _NotImplement()
    scroll_to: Callable = _NotImplement()
    add_screen: Callable = _NotImplement()
    catch_request: Callable = _NotImplement()
    switch_handler: Callable = _NotImplement()
    execute_script: Callable = _NotImplement()
    action_chains: Callable = _NotImplement()
    switch_to_frame: Callable = _NotImplement()
    switch_to_parent: Callable = _NotImplement()

    @property
    def debug(self) -> bool: raise NotImplementedError

    @property
    def webdriver(self) -> webdriver.Chrome: raise NotImplementedError


# 线程之间的数据隔离
class _PageProxy(threading.local):
    PROXY_ATTRIBUTE = "__page_object_proxy__"

    def __init__(self):
        setattr(self, self.PROXY_ATTRIBUTE, None)

    def __getattr__(self, item):
        if (page := self.get_proxy()) is None:
            raise RuntimeError("PageObject未实例化或未加载或者已被清理")
        return getattr(page, item)

    def __setattr__(self, key, value):
        if key == self.PROXY_ATTRIBUTE:
            return super().__setattr__(key, value)
        return setattr(self.get_proxy(), key, value)

    def __delattr__(self, key):
        if key == self.PROXY_ATTRIBUTE:
            self.clear_proxy()
        return delattr(self.get_proxy(), key)

    def get_proxy(self):
        return getattr(self, self.PROXY_ATTRIBUTE)

    def set_proxy(self, page):
        setattr(self, self.PROXY_ATTRIBUTE, page)

    def clear_proxy(self):
        setattr(self, self.PROXY_ATTRIBUTE, None)


p = _PageProxy()


# 路由管理器
class PageRouterManager:
    router_map = {}

    @classmethod
    def load_all_pages(cls):
        for module in load_pkg_modules(settings.PAGE_DIR):
            for m in dir(module):
                try:
                    attr = getattr(module, m)
                    if issubclass(attr, PageObject):
                        cls.register(attr)
                except TypeError:
                    pass

    @classmethod
    def register(cls, page_klass) -> None:
        for required_attr in PAGEOBJECT_BASE_ATTRS:
            try:
                attr = getattr(page_klass, required_attr)
            except AttributeError:
                raise AttributeError(f"{page_klass.__name__} 缺少 {required_attr} 配置") from None
            else:
                if attr is PAGEOBJECT_DEFAULT_VAL and page_klass.__name__ != BASE_PAGE:
                    raise ValueError(f"{page_klass.__name__}中{required_attr}未配置")

        page_name, page_path = page_klass.page_name, page_klass.page_path

        for k, v in cls.router_map.items():
            if k == page_name:
                raise NameError(f"已经存在名称为 {page_name} 的page对象")
            if v["page_path"] == page_path:
                raise NameError(f"{page_name} 已经是已经为 {page_path}")
            if v["page_klass"].__name__ == page_klass.__name__:
                raise NameError("存在同名的page类 %s" % page_klass.__name__)
        cls.router_map[page_name] = {
            "page_path": page_path,
            "page_klass": page_klass,
        }

    @classmethod
    def look_up(cls, name: str):
        try:
            v = cls.router_map[name]
        except KeyError:
            raise LookupError(f"不存在 {name} 页面") from None
        else:
            return v["page_klass"]

    @classmethod
    def get(cls, name: str) -> PageObject:
        try:
            v = cls.router_map[name]
        except KeyError:
            raise LookupError(f"不存在 {name} 页面") from None
        else:
            p.get(settings.HOST + v["page_path"])
            return v["page_klass"](p.webdriver)
